package edu.ubb.remeye.server.communication;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.statemachine.StateMachine;
import org.apache.mina.statemachine.StateMachineFactory;
import org.apache.mina.statemachine.StateMachineProxyBuilder;
import org.apache.mina.statemachine.annotation.IoHandlerTransition;
import org.apache.mina.statemachine.context.IoSessionStateContextLookup;
import org.apache.mina.statemachine.context.StateContext;
import org.apache.mina.statemachine.context.StateContextFactory;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.codecompany.jeha.ExceptionHandler;
import org.codecompany.jeha.core.HandlerUtil;

import edu.ubb.remeye.communication.message.codec.MessageCodecFactory;
import edu.ubb.remeye.exception.handler.RemEyeExceptionHandler;
import edu.ubb.remeye.gui.internationalization.LanguageManager;
import edu.ubb.remeye.server.Server;
import edu.ubb.remeye.server.communication.handler.ServerStateHandler;
import edu.ubb.remeye.server.settings.ServerSetter;

/**
 * Server class for communication.
 * 
 * @author Andrew Ianicicau
 *
 */
@ExceptionHandler(handler=RemEyeExceptionHandler.class)
public class RemEyeServer implements Server {

	///////////////////////////////////////////////////////////////////////////////////
	////	ATTRIBUTES - RemEyeServer
	///////////////////////////////////////////////////////////////////////////////////

	/**
	 * Server attribute for singleton design pattern.
	 */
	private static RemEyeServer server;
	
	/**
	 * The sever socket for accepting connections using Apache MINA IoAcceptor.
	 */
	private IoAcceptor clientAcceptor;

	/**
	 * The port where the server accepts connections. Default is 5900.
	 */
	private int serverPort;
	
	/**
	 * The status of server.
	 */
	private boolean serverRunning;

	
	
	///////////////////////////////////////////////////////////////////////////////////
	//// CONSTRUCTORS - RemEyeServer
	///////////////////////////////////////////////////////////////////////////////////

	private RemEyeServer() {
		serverRunning = false;
	}

	
	
	///////////////////////////////////////////////////////////////////////////////////
	////	METHODS - RemEyeServer
	///////////////////////////////////////////////////////////////////////////////////
	
	/**
	 * Getting server instance.
	 */
	public static RemEyeServer getServer() {
		if(server == null)
			server = new RemEyeServer();
		return server;
	}
	
	/**
	 * Start server.
	 * @throws ServerStartException 
	 */
	@Override
	public void startServer() {
		try {
			if(!server.serverRunning) {
				ServerSetter.getInstance().saveSettings();
				server.clientAcceptor = new NioSocketAcceptor();
				server.serverPort = ServerSetter.getInstance().getPort();
				server.clientAcceptor.setHandler(createServerIoHandler());
				server.clientAcceptor.getFilterChain().addLast("message", new ProtocolCodecFilter(new MessageCodecFactory()));
				server.clientAcceptor.bind(new InetSocketAddress(server.serverPort));			
				server.serverRunning = true;
			}
		} catch(IOException e) {
			String errorMessage = LanguageManager.getLanguageManager().getText("serverstarterror");
			HandlerUtil.handle(e, errorMessage);
		}
	}

	/**
	 * Stop server.
	 */
	@Override
	public void stopServer() {
		if(server.serverRunning) {
			server.clientAcceptor.unbind();
			server.clientAcceptor.dispose();
			server.serverRunning = false;
		}
	}

	/**
	 * Creating Apache MINA IoHandler with Apache MINA State Machine for server.
	 * @return IoHandler with state machine.
	 */
	private IoHandler createServerIoHandler() {
		// client state machine
		StateMachine sm = StateMachineFactory.getInstance(IoHandlerTransition.class).create(ServerStateHandler.BASE, new ServerStateHandler());

		return new StateMachineProxyBuilder().setStateContextLookup(
				new IoSessionStateContextLookup(new StateContextFactory() {

					@Override
					public StateContext create() {
						return new ServerStateHandler.ServerContext();
					}
					
				})).create(IoHandler.class, sm);
	}

}
